/**
 * 微信视频号数据拦截处理服务
 */

import moment from "moment/moment"

import type { PlasmoMessaging, PortName } from "@plasmohq/messaging"
import { Storage } from "@plasmohq/storage"

import {
  afterSaleDataMaps,
  ecDataMaps,
  interactDataMaps,
  productDataMaps,
  trafficDataMaps
} from "~/utils/wxDataMaps"

let wxPort: chrome.runtime.Port

// 使用port作为通信 name:wxHijackDataPort
chrome.runtime.onConnect.addListener((port) => {
  if (port.name === "wxHijackDataPort") {
    wxPort = port
    wxPort.onDisconnect.addListener(() => {
      wxPort = null
    })
  }
})

// 拦截数据缓存
let hijackData = {}
// 定时记录数据列表
let recordData = []
// 定时器
let recordTimer = null
// storage存储，记录数据更新时间戳
const storage = new Storage()
// 记录数据时间戳key
const _storageKey = "wxHijackDataTs"
// 记录条数Key
const _recordCountKey = "wxHijackRecordDataCount"

// 初始化服务缓存
const initCache = () => {
  hijackData = {}
  recordData = []
  storage.set(_storageKey, null).then()
  storage.set(_recordCountKey, 0).then()
}

// 更新hijackData缓存
const updateCache = (data: {}) => {
  hijackData = { ...hijackData, ...data }
  storage.set(_storageKey, Date.now()).then()
}

// 数据匹配键值转换写入
const transformDataMaps = (
  maps: {
    label: string
    key: string
    digital?: number
    cut?: boolean
    parent?: string
  }[],
  writeData: any,
  readData: any
) => {
  maps.forEach((item) => {
    writeData[item.key] = readData[item.key] || 0
    // 小数点保留
    if (item.digital) {
      writeData[item.key] = Number(
        Number(writeData[item.key]).toFixed(item.digital)
      )
    }
    // 数据截取
    if (item["cut"]) {
      const _value = Number(writeData[item.key] || 0)
      writeData[item.key] = _value > 0 ? Number((_value / 100).toFixed(2)) : 0
    }
  })
}

// 流量数据合并处理, 输出合并后的trafficData
const mergeTrafficData = () => {
  // 内置函数 递归查找hijackData["trafficTrendData"]的函数，并写入数据
  const _findLabelAndWrite = (
    _data: any[],
    _target: { label: string; value: number; key: string }
  ) => {
    for (const _item of _data) {
      if (_item.label === _target.label) {
        _item[_target.key] = _target.value
      }
      if (_item.sub.length > 0) {
        _findLabelAndWrite(_item.sub, _target)
      }
    }
  }
  // 以变量trafficTrendData为准，更新流量数据
  // 如果trafficTrendData为空, 则不处理
  if (Object.keys(hijackData["trafficTrendData"] || {}).length < 1) return
  // 查询hijackData中是否有channelTraffic流量数据
  // 如果流量数据为空，则不处理
  if (Object.keys(hijackData["channelTraffic"] || {}).length < 1) return
  // 从trafficTrendData中获取流量数据，合并到hijackData的channelTraffic中
  // trafficTrendData数据描述
  /**
   * {
   *      // 进房人次，主要获取
   *     "newWatchPv":[
   *        {
   *        // 数据列表
   *        "data":[
   *            {"ts":0,"value":"0"}
   *        ],
   *        // 维度列表，层级列表，按层级排列，最后一项为目标元素
   *        "dimensions":[
   *            {
   *                "type":"",
   *                "value":"",
   *                // 匹配标签
   *                "uxLabel":""
   *            }
   *        ],
   *        // 当step大于60时，为最终值, dimensions中只有两个元素
   *        "step": "",
   *        "beginTs":"",
   *        "endTs":""
   *        }
   *     ]
   * }
   */
  try {
    const trafficTrendData = hijackData["trafficTrendData"]
    const channelTrafficData = Array.from(hijackData["channelTraffic"] || [])
    trafficTrendData["newWatchPv"].forEach((item: any) => {
      // 找出step大于60的元素
      if (Number(item["step"]) > 60) {
        // 获取dimension最后一个元素的uxLabel标签
        const _label =
          item["dimensions"][item["dimensions"].length - 1]["uxLabel"]
        // 获取data列表最后一个元素的value值
        const _value = item["data"][item["data"].length - 1]["value"]
        // 查找hijackData["channelTraffic"]中是否有对应标签的流量数据
        _findLabelAndWrite(channelTrafficData, {
          label: _label,
          value: Number(_value),
          key: "pv"
        })
      }
    })
    // 清空trafficTrendData
    return channelTrafficData || []
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
    return []
  }
}

/**
 * 请求数据拦截解析器方法
 */
// dashboard_data拦截
const dashboardDataFilter = (url: string, response: any, params?: any) => {
  // 正则匹配url
  const urlReg = new RegExp(
    `^https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/statistic/get_ec_conversion_dashboard_data_v3`
  )
  if (!urlReg.test(url)) return true
  // 处理数据
  try {
    const tempData = {
      trafficData: {},
      interactData: {}
    }
    const data = response.data
    // 基础数据
    // 直播状态
    tempData["isLiving"] = Boolean(data["isLiving"])
    // 直播间名称
    tempData["liveRoom"] = data["anchorNickname"] || ""
    // 开播时间
    tempData["liveStartTime"] = data["createTime"] || 0
    // 结束时间
    if (!tempData["isLiving"]) {
      tempData["liveEndTime"] = data["endTime"] || 0
    }
    const overviewData = data["overview"] || {}
    // 流量指标数据处理
    transformDataMaps(trafficDataMaps, tempData.trafficData, overviewData)
    // 互动指标数据处理
    transformDataMaps(interactDataMaps, tempData.interactData, overviewData)
    // 记录渠道趋势数据
    tempData["trafficTrendData"] = data["trendingSource"]
    // }
    // 更新hijackData
    updateCache(tempData)
    if (tempData["trafficData"]) {
      // 合并流量数据
      mergeTrafficData()
    }
    // 如果tempData["trafficTrendData"]不存在，返回false
    return Boolean(tempData["trafficTrendData"])
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
    return true
  }
}
// ec data拦截
const ecDataFilter = (url: string, response: any, params?: any) => {
  // 正则匹配url
  const urlReg = new RegExp(
    `^https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/statistic/get_live_ec_data_summary`
  )
  if (!urlReg.test(url)) return true
  try {
    const tempData = {
      ecData: {},
      afterSaleData: {}
    }
    const ecData = response.data || {}
    // 累计成交金额
    let gmv = Number(ecData["totalGmv"] || 0)
    tempData["totalGmv"] = gmv > 0 ? Number((gmv / 100).toFixed(2)) : 0
    // 交易指标数据处理
    transformDataMaps(ecDataMaps, tempData.ecData, ecData)
    // 售后指标数据处理
    transformDataMaps(afterSaleDataMaps, tempData.afterSaleData, ecData)
    // 更新hijackData
    updateCache(tempData)
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
  }
  return true
}
// 内容力数据拦截
const contentPowerDataFilter = (url: string, response: any, params?: any) => {
  // 正则匹配url
  const urlReg = new RegExp(
    `^https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/svrkit/MMFinderLiveDashboardDatasvr/getLiveComparisonIndex`
  )
  if (!urlReg.test(url)) return true
  try {
    const tempData = {
      contentPower: {}
    }
    const cpData = response.data || {}
    // 内容力数据处理
    const isLive = cpData["isLiving"]
    // 通过直播状态确定数据key
    tempData["contentPower"] = cpData[isLive ? "onAir" : "ended"] || {}
    // 更新hijackData
    updateCache(tempData)
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
  }
  return true
}
// 转化力数据拦截
const conversionPowerDataFilter = (
  url: string,
  response: any,
  params?: any
) => {
  // 正则匹配url
  const urlReg = new RegExp(
    `^https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/svrkit/MmFinderECAssistantDataSvr/getLiveEcConversionMetric`
  )
  if (!urlReg.test(url)) return true
  try {
    const tempData = {
      conversionPower: {}
    }
    const cpData = response.data
    tempData["conversionPower"] = cpData["wholeLiveConversion"]
    // 更新hijackData
    updateCache(tempData)
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
  }
  return true
}
// 渠道数据拦截
const channelTrafficDataFilter = (url: string, response: any, params?: any) => {
  // 正则匹配url
  const urlReg = new RegExp(
    `^https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/svrkit/MmFinderECAssistantDataSvr/getLiveDistributionChannel`
  )
  if (!urlReg.test(url)) return true
  try {
    const tempData = {
      channelTraffic: []
    }
    const channelData = response.data
    const _channels = channelData["liveDistChannelSourceStats"]
    // 顶层type对应的label map
    const level0TypeMap = {
      1: "自然流量",
      2: "加热流量",
      3: "广告流量"
    }
    // tempData["channelTraffic"]格式重构
    _channels.forEach((item: any) => {
      const _temp = {
        label: level0TypeMap[item["liveDstChannelType"]],
        gmv: Number(item["gmv"] || 0),
        uv: Number(item["uv"] || 0),
        level: 0,
        sub: []
      }
      // 二级维度数据处理
      const subItem = item["channelSourceStats"] || []
      subItem.forEach((sItem: any) => {
        const _sub = {
          label: sItem["sourceChannelName"],
          gmv: Number(sItem["gmv"] || 0),
          uv: Number(sItem["uv"] || 0),
          level: 1,
          sub: []
        }
        const subSubItem = subItem["channelSourceStats"] || []
        // 三级维度数据处理
        subSubItem.forEach((ssItem: any) => {
          const _subSub = {
            label: ssItem["sourceChannelName"],
            gmv: Number(ssItem["gmv"] || 0),
            uv: Number(ssItem["uv"] || 0),
            level: 2
          }
          _sub.sub.push(_subSub)
        })
        _temp.sub.push(_sub)
      })
      tempData["channelTraffic"].push(_temp)
    })
    // 更新hijackData
    updateCache(tempData)
    // 合并流量数据
    mergeTrafficData()
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
  }
  return true
}
// 商品列表数据拦截
const productDataFilter = (url: string, response: any, params?: any) => {
  // 正则匹配url
  const urlReg = new RegExp(
    `^https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/statistic/get_single_live_ec_spu_data_page_v2`
  )
  if (!urlReg.test(url)) return true
  try {
    const tempData = {
      product: []
    }
    const productData = response.data
    tempData.product = productData["spuDataList"]
    // 对比产品记录数量，如果比hijackData中记录多，则更新
    if (
      !hijackData["product"] ||
      tempData.product.length > hijackData["product"].length
    ) {
      updateCache(tempData)
    }
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
  }
  return true
}
// 商品趋势数据拦截
const productTrendDataFilter = (url: string, response: any, params?: any) => {
  // 正则匹配url
  const urlReg = new RegExp(
    `^https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin/statistic/get_single_commodity_trend`
  )
  if (!urlReg.test(url)) return true
  try {
    // 请求的商品id
    const { commodityId } = params || {}
    // 获取商品上架时间(任何一个趋势数据中的首项)
    const clkUvTrend = response.data["clkUvTrend"] || []
    // 获取上架时间
    const firstTs = clkUvTrend[0]["ds"] || 0
    // 写入到hijackData的对应的商品项中
    const productData = hijackData["product"] || []
    productData.forEach((item: any) => {
      if (item["baseData"]["spuId"] === commodityId) {
        item["baseData"]["firstTs"] = moment
          .unix(Number(firstTs))
          .format("YYYY-MM-DD HH:mm:ss")
      }
    })
    updateCache({ product: productData })
    wxPort.postMessage({
      type: "product",
      data: commodityId
    })
  } catch (e) {
    console.log("dg-assistant-error:视频号数据拦截数据解析异常", e)
  }
  return true
}
// 视频号网络请求数据解析器集合处理Hub
const hijackDataFilterHub = (url: string, response: any, params: any) => {
  let parserRes = true
  const filterList = [
    dashboardDataFilter,
    ecDataFilter,
    contentPowerDataFilter,
    conversionPowerDataFilter,
    channelTrafficDataFilter,
    productDataFilter,
    productTrendDataFilter
  ]
  for (let i = 0; i < filterList.length; i++) {
    if (!filterList[i](url, response, params)) {
      parserRes = false
    }
  }
  return parserRes
}
/**
 * 数据导出处理相关方法
 */
// hijack数据格式化为导出数据行
const formatHijackData = (data: any) => {
  const tempData = {}
  const colHeader = ["直播间", "开播时间", "下播时间", "累计成交金额"]
  // 基础中文键map
  const cnBaseKeyMap = {
    liveRoom: "直播间",
    liveStartTime: "开播时间",
    liveEndTime: "下播时间",
    totalGmv: "累计成交金额"
  }
  // 基础键值转换为中文
  for (const key in cnBaseKeyMap) {
    let value = data[key] || ""
    if ((key == "liveStartTime" || key == "liveEndTime") && value !== "") {
      value = moment.unix(Number(value)).format("YYYY-MM-DD HH:mm:ss")
    }
    tempData[cnBaseKeyMap[key]] = value
  }
  // 流量指标数据
  const trafficData = data["trafficData"] || {}
  for (const kItem of trafficDataMaps) {
    colHeader.push(kItem.label)
    tempData[kItem.label] = trafficData[kItem.key] || 0
  }
  // 互动指标数据
  const interactData = data["interactData"] || {}
  for (const kItem of interactDataMaps) {
    colHeader.push(kItem.label)
    tempData[kItem.label] = interactData[kItem.key] || 0
  }
  // 交易指标数据
  const ecData = data["ecData"] || {}
  for (const kItem of ecDataMaps) {
    colHeader.push(kItem.label)
    tempData[kItem.label] = ecData[kItem.key] || 0
  }
  // 售后指标数据
  const afterSaleData = data["afterSaleData"] || {}
  for (const kItem of afterSaleDataMaps) {
    colHeader.push(kItem.label)
    tempData[kItem.label] = afterSaleData[kItem.key] || 0
  }
  // 渠道流量数据
  const channelData = mergeTrafficData()
  if (channelData) {
    // 以防没有获取渠道流量数据
    // channel数据转换内部方法
    const _formatChannelData = (item: any, top?: string) => {
      const _tMaps = {
        gmv: "gmv",
        pv: "进房人次",
        uv: "观看人数"
      }
      for (const _k in _tMaps) {
        const headerKey = `${top ? top + "|" : ""}${item.label}(${_tMaps[_k]})`
        colHeader.push(headerKey)
        tempData[headerKey] = item[_k]
      }
    }
    channelData.forEach((item: any) => {
      _formatChannelData(item)
      if (item.sub && item.sub.length > 0) {
        item.sub.forEach((subItem: any) => {
          _formatChannelData(subItem, item.label)
          if (subItem.sub && subItem.sub.length > 0) {
            subItem.sub.forEach((_subItem: any) => {
              _formatChannelData(_subItem, `${item.label}|${subItem.label}`)
            })
          }
        })
      }
    })
  }

  // 内容力数据
  const _contentPowerMaps = {
    recommendEffectiveNewWatch2UvOverImpressionUv: "内容力|直播有效进房率",
    averageWatchSeconds: "内容力|人均看播时长",
    commentUvOverNewWatchUv: "内容力|评论率",
    likeUvOverNewWatchUv: "内容力|点赞率"
  }
  const contentPowerData = data["contentPower"] || {}
  for (const _k in contentPowerData) {
    colHeader.push(_contentPowerMaps[_k])
    tempData[_contentPowerMaps[_k]] = contentPowerData[_k]["valueFloat"] || 0
  }
  // 转化力数据
  const _conversionPowerMaps = {
    productViewClickConversionRatio: "转化力|产品气泡点击率(次数)",
    bubbleViewClickConversionRatio: "转化力|购物袋点击率(次数)",
    payConversionRatio: "转化力|成交转化率",
    kViewPayConversionRatio: "转化力|千次观看成交金额",
    productListClickConversionRatio: "转化力|购物袋商品点击率(次数)"
  }
  const conversionPowerData = data["conversionPower"]
  for (const _k in _conversionPowerMaps) {
    colHeader.push(_conversionPowerMaps[_k])
    tempData[_conversionPowerMaps[_k]] =
      conversionPowerData[_k]["metricValue"] || 0
    if (_k == "kViewPayConversionRatio") {
      tempData[_conversionPowerMaps[_k]] = Number(
        tempData[_conversionPowerMaps[_k]]
      ).toFixed(2)
    }
  }

  return { data: tempData, header: colHeader }
}
// 数据导出
const exportExcelData = () => {
  try {
    // 数据处理
    // 判断是否有定时记录数据
    const _exData = []
    let _exHeader = []

    // 格式化数据
    recordData.forEach((item) => {
      const _tempData = formatHijackData(item.data)
      _tempData.data["记录时间"] = item.recordTime
      _exData.push(_tempData.data)
      _exHeader = _tempData.header
    })
    _exHeader = ["记录时间"].concat(_exHeader)
    if (!hijackData["isLiving"]) {
      // 当前为已下播的数据记录，直接使用hijackData
      const _endExData = formatHijackData(hijackData)
      const _exRes = _endExData.data
      _exRes["记录时间"] = _exRes["下播时间"]
      _exData.push(_exRes)
      _exHeader = ["记录时间"].concat(_endExData.header)
    }
    // 返回数据列和头部列数据
    return {
      data: _exData,
      header: _exHeader
    }
  } catch (e) {
    return {
      data: [{ error: e }],
      header: ["error"]
    }
  }
}

// 商品数据导出
const exportProductExcelData = () => {
  try {
    const _exData = []
    let _exHeader = []
    const productData = hijackData["product"] || []
    // 生成header
    productDataMaps.forEach((item: any) => {
      _exHeader.push(item.label)
    })
    // 生成数据列
    productData.forEach((item: any) => {
      const _tempData = {}
      productDataMaps.forEach((kItem: any) => {
        let _value = null
        const _key = kItem.label
        // parentKey判断
        if (kItem.parent) {
          const parent = item[kItem.parent] || {}
          _value = parent[kItem.key]
        } else {
          _value = item[kItem.key]
        }
        if (_value) {
          // 数据处理
          if (kItem.digital) {
            // 保留小数位
            _value = Number(_value).toFixed(2)
          }
          // 数据截取
          if (kItem.cut) {
            // 去除后两位
            _value = Number(Number(_value) / 100).toFixed(0)
          }
        }
        _tempData[_key] = _value
      })
      _exData.push(_tempData)
    })
    return {
      data: _exData,
      header: _exHeader
    }
  } catch (e) {
    return {
      data: [{ error: e }],
      header: ["error"]
    }
  }
}

/**
 * 定时记录处理相关方法
 */
// 插入一条定时记录
const insertRecordData = (recordTime?: string) => {
  // 当前时间
  const _recordTime = moment().format("YYYY-MM-DD HH:mm:ss")
  recordData.push({
    recordTime: recordTime || _recordTime,
    data: { ...hijackData }
  })
  // 更新记录条数
  storage.set(_recordCountKey, recordData.length).then()
}
// 开始定时记录
const startRecordData = (intervalMinute: number) => {
  insertRecordData()
  recordTimer = setInterval(
    () => {
      insertRecordData()
    },
    intervalMinute * 60 * 1000
  )
  return true
}
// 停止定时记录
const stopRecordData = () => {
  clearInterval(recordTimer)
  return true
}

/**
 * 视频号数据大屏请求拦截数据处理服务
 * 根据req.body中的action标记，对应处理不同请求
 * action:
 *    1. hijack  解析视频号数据并缓存
 * @param req
 * @param res
 */
const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
  const { action, data } = req.body
  let sendData = {}
  switch (action) {
    // 初始化
    case "init":
      initCache()
      break
    // 拦截解析
    case "hijack":
      const { url, response, params } = data
      const hijackRes = hijackDataFilterHub(url, response, params)
      sendData["hijackRes"] = hijackRes
      break
    // 数据导出
    case "export":
      sendData = exportExcelData()
      break
    // 商品数据导出
    case "exportProduct":
      sendData = exportProductExcelData()
      break
    // 数据获取
    case "getData":
      sendData = { ...hijackData }
      break
    // 开始记录数据
    case "startRecord":
      const { intervalMinute } = data
      sendData["res"] = startRecordData(intervalMinute)
      break
    // 停止记录数据
    case "stopRecord":
      sendData["res"] = stopRecordData()
      break
    default:
      break
  }
  res.send(sendData)
}

export default handler
